﻿
#ifdef AS_REDIS
#include "as_redis_result.h"
#include "as_redis_value.h"


as::db::RedisResult *as::db::RedisResult::SetError(const char *str)
{
	error_desc_ = str;
	return this;
}

as::db::RedisResult *as::db::RedisResult::SetErrorCode(RedisError errorcode)
{
	error_code_ = errorcode;
	return this;
}

as::db::RedisResult *as::db::RedisResult::SetReply(redisReply *reply)
{
	reply_ = reply;
	return this;
}

as::db::RedisResult *as::db::RedisResult::SetCommand(std::string &cmd)
{
	command_ = cmd;
	return this;
}

const std::string &as::db::RedisResult::GetErrorDesc() const
{
	return error_desc_;
}

as::db::RedisError as::db::RedisResult::GetErrrorCode() const
{
	return error_code_;
}

const std::string &as::db::RedisResult::GetCommand() const
{
	return command_;
}

int as::db::RedisResult::GetElementCount() const
{
	if (reply_ == nullptr)
		return 0;
	return (int)reply_->elements;
}

bool as::db::RedisResult::GetResult(int &out)
{
	if (reply_ == nullptr)
		return false;
	if (reply_->type == REDIS_REPLY_NIL || reply_->type != REDIS_REPLY_INTEGER)
		return false;

	out = reply_->integer;
	return true;
}

bool as::db::RedisResult::MoveResult(redisReply *reply, RedisValue &out)
{
	bool result = false;
	switch (reply->type)
	{
	case REDIS_REPLY_DOUBLE:
	{
		out = reply->dval;
		result = true;
		break;
	}
	case REDIS_REPLY_INTEGER:
	{
		out = reply->integer;
		result = true;
		break;
	}
	case REDIS_REPLY_STRING:
	{
		out = reply->str;
		result = true;
		break;
	}
	case REDIS_REPLY_BOOL:
	{
		out = (bool)reply->integer;
		result = true;
		break;
	}
	case REDIS_REPLY_NIL:
	{
		result = true;
		break;
	}
	default:
		result = false;
	}

	return result;
}

bool as::db::RedisResult::GetResult(bool &out)
{
	if (reply_ == nullptr)
		return false;
	if (reply_->type == REDIS_REPLY_NIL || reply_->type != REDIS_REPLY_BOOL)
		return false;
	
	out = reply_->integer;
	return true;
}

bool as::db::RedisResult::GetResult(std::vector<RedisValue> &out)
{
	if (reply_ == nullptr)
		return false;

	if (reply_->type == REDIS_REPLY_NIL)
		return false;

	if (reply_->type != REDIS_REPLY_ARRAY)
		return false;

	for (int i = 0; i < reply_->elements; ++i)
	{
		auto ele = reply_->element[i];
		RedisValue val;
		if (MoveResult(reply_, val))
		{
			out.emplace_back(val);
		}
		else
		{
			return false;
		}
	}

	return true;
}

bool as::db::RedisResult::GetResult(std::unordered_map<std::string, RedisValue> &out)
{
	if (reply_ == nullptr)
		return false;

	if (reply_->type == REDIS_REPLY_NIL)
		return false;

	if (reply_->type != REDIS_REPLY_ARRAY)
		return false;

	for (int i = 0; i < reply_->elements; i += 2)
	{
		if (i + 1 >= reply_->elements)
			return false;

		auto ele = reply_->element[i];
		std::string field(ele->str, ele->len);
		RedisValue val;
		if (MoveResult(reply_, val))
		{
			out[std::move(field)] = std::move(val);
		}
		else
		{
			return false;
		}
	}
	return true;
}

bool as::db::RedisResult::GetResult(std::map<std::string, RedisValue> &out)
{
	if (reply_ == nullptr)
		return false;

	if (reply_->type == REDIS_REPLY_NIL)
		return false;

	if (reply_->type != REDIS_REPLY_ARRAY)
		return false;

	for (int i = 0; i < reply_->elements; i += 2)
	{
		if (i + 1 >= reply_->elements)
			return false;

		auto ele = reply_->element[i];
		std::string field(ele->str, ele->len);
		RedisValue val;
		if (MoveResult(reply_, val))
		{
			out[std::move(field)] = std::move(val);
		}
		else
		{
			return false;
		}
	}
	return true;
}

bool as::db::RedisResult::GetResult(RedisValue &out)
{
	if (reply_ == nullptr)
		return false;
	return MoveResult(reply_, out);
}

bool as::db::RedisResult::GetResult(std::string &out)
{
	if (reply_ == nullptr)
		return false;

	if (reply_->type == REDIS_REPLY_NIL)
		return false;

	if (reply_->type == REDIS_REPLY_STRING)
	{
		out = reply_->str;
		return true;
	}
	return false;
}

bool as::db::RedisResult::GetResult(float &out)
{
	if (reply_ == nullptr)
		return false;

	if (reply_->type == REDIS_REPLY_NIL)
		return false;
	bool result = false;
	if (reply_->type == REDIS_REPLY_DOUBLE)
	{
		out = static_cast<float>(reply_->dval);
		result = true;
	}
	else
	{
		try
		{
			out = std::stof(reply_->str);
			result = true;
		}
		catch (const std::exception &)
		{
			result = false;
		}
	}
	return result;
}

bool as::db::RedisResult::GetResult(double &out)
{
	if (reply_ == nullptr)
		return false;
	
	if (reply_->type == REDIS_REPLY_NIL)
		return false;
	bool result = false;
	if (reply_->type == REDIS_REPLY_DOUBLE)
	{
		out = reply_->dval;
		result = true;
	}
	else
	{
		try
		{
			out = std::stod(reply_->str);
			result = true;
		}
		catch (const std::exception &)
		{
			result = false;
		}
	}
	return result;
}

bool as::db::RedisResult::GetResult(uint64_t &out)
{
	if (reply_ == nullptr)
		return false;
	if (reply_->type == REDIS_REPLY_NIL || reply_->type != REDIS_REPLY_INTEGER)
		return false;

	out = static_cast<uint64_t>(reply_->integer);
	return true;
}

bool as::db::RedisResult::GetResult(int64_t &out)
{
	if (reply_ == nullptr)
		return false;
	if (reply_->type == REDIS_REPLY_NIL || reply_->type != REDIS_REPLY_INTEGER)
		return false;

	out = reply_->integer;
	return true;
}

bool as::db::RedisResult::GetResult(unsigned int &out)
{
	if (reply_ == nullptr)
		return false;
	if (reply_->type == REDIS_REPLY_NIL || reply_->type != REDIS_REPLY_INTEGER)
		return false;

	out = static_cast<unsigned int>(reply_->integer);
	return true;
}

#endif
